<template>
  <div class="bg-white border p-1 rounded border-gray-200">
    <div class="mb-1">卖出</div>
    <audio ref="audioSuccessMax">
      <source src="./audio/sold-price-max.mp3" type="audio/ogg">
    </audio>
    <audio ref="audioSuccessMin">
      <source src="./audio/sold-price-min.mp3" type="audio/ogg">
    </audio>
    <a-form label-align="left" :label-col="{span:8}" :wrapper-col="{span:16}">
      <a-form-item label="止盈百分比" class="!mb-1">
        <a-input-number addon-after="%"
          v-model:value="priceMaxPercent"
          :min="0" :step="0.1"
          :disabled="null !== orderId"
          @change="actionPriceMaxPercentChange"
        ></a-input-number>
      </a-form-item>

      <a-form-item label="止盈价格" class="!mb-1">
        <a-input :value="priceMax.toFixed(8)" disabled />
      </a-form-item>

      <a-form-item label="止损百分比" class="!mb-1">
        <a-input-number addon-after="%"
          v-model:value="priceMinPercent"
          :min="0" :step="0.1"
          :disabled="null !== orderId"
          @change="actionPriceMinPercentChange"
        ></a-input-number>
      </a-form-item>

      <a-form-item label="止损价格" class="!mb-1">
        <a-input :value="priceMin.toFixed(8)" disabled />
      </a-form-item>

      <a-form-item label="卖出数量" class="!mb-1">
        <a-input-number class="!w-full" v-model:value="quantity"
          :disabled="props.disabled"
        ></a-input-number>
      </a-form-item>

      <a-form-item v-if="orderDuration" label="挂单时长" class="!mb-1">
        <a-input :value="orderDuration" disabled />
      </a-form-item>

      <a-form-item v-if="orderFillSize" label="成交量" class="!mb-1">
        <a-input :value="orderFillSize" disabled />
      </a-form-item>

      <a-form-item class="!mb-1 !mt-2" :wrapper-col="{span:24}">
        <a-button class="w-full" type="primary"
          :loading="orderId != null"
          :disabled="quantity <= 0 || props.disabled"
          @click="actionSell"
        >卖出</a-button>
      </a-form-item>

      <a-form-item v-if="null !== orderId && '' !== orderId" class="!mb-1" :wrapper-col="{span:24}">
        <a-button class="w-full" danger :loading="orderIsCanceling" @click="actionOrderCancel">撤单</a-button>
      </a-form-item>
    </a-form>
  </div>
</template>
<script setup>
import { ref, watch, inject, nextTick } from 'vue';
import { Modal, message } from 'ant-design-vue';
import OkxRestService from './OkxRestService.js';
import Common from './Common.js';
import Decimal from 'decimal.js';
/** @var {Function} */
const emits = defineEmits(['start','success','failed']);
/** @var {Object} */
const props = defineProps({
    instId : {type: String,default: 'BTC-USDT'},
    basePrice: {type: Decimal,default: 0},
    boughtPrice : {type: Decimal,default: 0},
    currentPrice : {type: Decimal,default: 0},
    quantity : {type: Decimal,default: 0},
    disabled : {type: Boolean,default: false},
});
/** @var {number} */
const priceMaxPercent = ref(0.5);
/** @var {number} */
const priceMax = ref(new Decimal(0));
/** @var {number} */
const priceMinPercent = ref(1.0);
/** @var {number} */
const priceMin = ref(new Decimal(0));
/** @var {number} */
const quantity = ref(0);
/** @var {String} */
const orderId = ref(null);
/** @var {String} */
const orderDuration = ref(null);
/** @var {String} */
const orderFillSize = ref(null);
/** @var {TickerChart} */
const tickerChart = inject('tickerChart');
/** @var {boolean} */
const orderIsCanceling = ref(false);
/** @var {HtmlElement} */
const audioSuccessMax = ref(null);
/** @var {HtmlElement} */
const audioSuccessMin = ref(null);
/** @var {boolean} */
let isManualCanceled = ref(false);
// update price
watch(() => [props.basePrice, props.boughtPrice, priceMaxPercent.value, priceMinPercent.value], handlePriceUpdateRequired);
// update quantity
watch(() => props.quantity, handlePropsQuantityChange, { immediate: true });
// define expose
defineExpose({sell});

// generate sell order
async function sell() {
    await actionSell();
}

// 订单监视并处理
async function orderMonitorAndProcess( options ) {
    let service = OkxRestService.getInstance();
    let orderInfo = await service.tradeOrderAlgoGet(options.algoId);
    if ( null === orderInfo ) {
        orderInfo = await service.tradeOrderAlgoGetFromHistory(options.algoId);
    }

    // 订单状态为挂单
    if ( 'live' === orderInfo.state ) {
        orderDuration.value = Common.numberFormatAsDuration((Date.now() - options.startedAt) / 1000);
        await Common.sleep(1000);
        return false ;
    }

    // 订单状态为已成交
    if (orderInfo.state == 'effective' ) {
        let now = Date.now();
        tickerChart.value.markPointSet(`sell-${now}`, now, props.currentPrice.toString(), {symbol: 'circle', color:'red'});
        if ( 'tp' === orderInfo.actualSide ) {
            message.success("卖出 : 已卖出（止盈）");
            audioSuccessMax.value.play();
        } else {
            message.success("卖出 : 已卖出（止损）");
            audioSuccessMin.value.play();
        }

        emits('success');
        return true;
    }

    // 订单状态为已撤单
    if ( 'canceled' === orderInfo.state ) {
        message.success("卖出 : 已撤单");
        emits('failed');
        return true;
    }

    Modal.error({content:`订单状态异常: ${orderInfo.state}`});
    return true;
}


// handle price update required
function handlePriceUpdateRequired() {
    let newPrice = props.basePrice;
    if ( !props.boughtPrice.equals(0) ) {
        newPrice = props.boughtPrice;
    }

    priceMax.value = newPrice.add(newPrice.times(new Decimal(priceMaxPercent.value).div(100)));
    tickerChart.value.markLineSet('sell-max', priceMax.value.toString(), {name:'止盈', color:'green'});

    priceMin.value = newPrice.minus(newPrice.times(new Decimal(priceMinPercent.value).div(100)));
    tickerChart.value.markLineSet('sell-min', priceMin.value.toString(), {name:'止损', color:'red'});
}

// handle props quantity change
function handlePropsQuantityChange( newValue ) {
    quantity.value = newValue.toFixed(8) * 1;
}

// action sell
async function actionSell() {
    emits('start');
    await nextTick();

    isManualCanceled = false;
    orderId.value = '';
    tickerChart.value.markLineSet('sell-max', priceMax.value.toString(), {name:'止盈', color:'green'});
    tickerChart.value.markLineSet('sell-min', priceMin.value.toString(), {name:'止损', color:'red'});

    // 卖出下单
    let service = OkxRestService.getInstance();
    let order = null;
    try {
        order = await service.tradeOrderAlgoOcoSell(
            props.instId,
            quantity.value,
            priceMax.value.toString(),
            priceMin.value.toString(),
        );
        orderId.value = order.algoId;
        message.success("卖出 : 已下单");
    } catch ( e ) {
        emits('failed');
        orderId.value = null;
        Modal.error({title: '下单失败', content: e.message});
        return ;
    }

    // 监视订单状态
    let timeStart = Date.now();
    let hasProcessed = false;
    do {
        let mpOptions = {};
        mpOptions.startedAt = timeStart;
        mpOptions.algoId = order.algoId;
        hasProcessed = await orderMonitorAndProcess(mpOptions);
    } while( !hasProcessed );

    // 清理卖出状态
    orderDuration.value = null;
    orderFillSize.value = null;
    orderId.value = null;
}

// action order cancel
async function actionOrderCancel() {
    orderIsCanceling.value = true;
    let service = OkxRestService.getInstance();
    try {
        isManualCanceled = true;
        await service.tradeOrderAlgoCancel(props.instId, orderId.value);
    } catch ( e ) {
        isManualCanceled = false;
        orderIsCanceling.value = false;
        Modal.error({title: '撤单失败', content: e.message});
        return ;
    }
    orderId.value = null;
    orderDuration.value = null;
    orderFillSize.value = null;
    orderIsCanceling.value = false;
}

// action on price max percent change
function actionPriceMaxPercentChange() {
    tickerChart.value.yAxisRangeAutoFit();
}

// action on price min percent change
function actionPriceMinPercentChange() {
    tickerChart.value.yAxisRangeAutoFit();
}
</script>
